package cn.wolfcode.p2p.business.service.impl;

import cn.wolfcode.p2p.base.domain.Account;
import cn.wolfcode.p2p.base.domain.BaseAuditDomain;
import cn.wolfcode.p2p.base.domain.LoginInfo;
import cn.wolfcode.p2p.base.domain.UserInfo;
import cn.wolfcode.p2p.base.query.QueryObject;
import cn.wolfcode.p2p.base.service.IAccountService;
import cn.wolfcode.p2p.base.service.IUserInfoService;
import cn.wolfcode.p2p.business.domain.*;
import cn.wolfcode.p2p.business.mapper.BidRequestMapper;
import cn.wolfcode.p2p.business.query.BidRequestQuery;
import cn.wolfcode.p2p.business.service.*;
import cn.wolfcode.p2p.util.*;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;

@Service
@Transactional
public class BidRequestServiceImpl implements IBidRequestService, ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private BidRequestMapper bidRequestMapper;
    @Autowired
    private IUserInfoService userInfoService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IBidRequestAuditHistoryService bidRequestAuditHistoryService;
    @Autowired
    private IBidService bidService;
    @Autowired
    private IAccountFlowService accountFlowService;
    @Autowired
    private IPaymentScheduleService paymentScheduleService;
    @Autowired
    private ISystemAccountService systemAccountService;
    @Autowired
    private ISystemAccountFlowService systemAccountFlowService;
    @Autowired
    private IPaymentScheduleDetailService paymentScheduleDetailService;
    @Autowired
    private ICreditTransferService creditTransferService;

    /**
     * 缓存序列
     */
    public static final ConcurrentLinkedQueue<BidRequest> linkedQueue = new ConcurrentLinkedQueue();


    @Override
    public void apply(BidRequest bidRequest) {
        //判断数据
        LoginInfo currentUser = UserContext.getCurrentUser();
        Account account = accountService.getById(currentUser.getId());
        AssertUtil.instance().isFalse(bidRequest.getBidRequestAmount().compareTo(Constants.BORROW_MIN_AMOUNT) < 0, "借款金额最少为:" + Constants.BORROW_MIN_AMOUNT + "元")
                .isFalse(bidRequest.getBidRequestAmount().compareTo(account.getBorrowLimitAmount()) > 0, "借款金额最多为:" + Constants.DEFAULT_BORROW_LIMIT + "元")
                .isFalse(bidRequest.getMinBidAmount().compareTo(Constants.BID_MIN_AMOUNT) < 0, "投标金额最少为:" + Constants.BID_MIN_AMOUNT + "元")
                .isFalse(bidRequest.getCurrentRate().compareTo(Constants.BORROW_MIN_RATE) < 0, "借款利率最少为:" + Constants.BORROW_MIN_RATE + "%")
                .isFalse(bidRequest.getCurrentRate().compareTo(Constants.BORROW_MAX_RATE) > 0, "借款利率最多为:" + Constants.BORROW_MAX_RATE + "%")
                .isNotNull(bidRequest.getTitle(), "借款标题必填")
                .isNotNull(bidRequest.getDescription(), "借款申请描述必填");
        //判断是否在借款流程中,且通过了三大认证
        UserInfo userInfo = userInfoService.getById(currentUser.getId());
        AssertUtil.instance().isFalse(userInfo.hasBorrowInProcess(), "老铁,您已经有借款申请了")
                .isFalse(!userInfo.canBorrow(), "老铁,您还没通过三大认证");
        //为一个申请设置值
        BidRequest bid = new BidRequest();
        bid.setReturnType(bidRequest.getReturnType());
        bid.setBidRequestAmount(bidRequest.getBidRequestAmount());
        bid.setCurrentRate(bidRequest.getCurrentRate());
        bid.setMonthes2Return(bidRequest.getMonthes2Return());
        bid.setTitle(bidRequest.getTitle());
        bid.setDescription(bidRequest.getDescription());
        bid.setCreateUser(currentUser);
        bid.setDisableDays(bidRequest.getDisableDays());
        bid.setMinBidAmount(bidRequest.getMinBidAmount());
        bid.setApplyTime(new Date());
        BigDecimal totalRewardAmount = CalculatetUtil.calTotalInterest(bidRequest.getReturnType(), bidRequest.getBidRequestAmount(), bidRequest.getCurrentRate(), bidRequest.getMonthes2Return());
        bid.setTotalRewardAmount(totalRewardAmount);

        //保存一个申请
        bidRequestMapper.insert(bid);

        //改变用户的位状态
        userInfo.addBitState(BitStatesUtil.HAS_BIDREQUEST_IN_PROCESS);
        userInfoService.update(userInfo);
    }

    @Override
    public PageInfo query(QueryObject qo) {
        PageHelper.startPage(qo.getCurrentPage(), qo.getCurrentPage(), qo.getOrderBy());
        List<BidRequest> list = bidRequestMapper.selectForList(qo);
        return new PageInfo(list);
    }

    @Override
    public void publisAudit(Long id, Integer state, String remark, Date publishTime) {
        //判断参数
        BidRequest br = bidRequestMapper.selectByPrimaryKey(id);
        //是否处于待审核状态
        AssertUtil.instance().isFalse(br.getBidRequestState() != Constants.BIDREQUEST_STATE_PUBLISH_AUDITING, "借款不处于待审核状态");
        //设置公共信息,保存到审核历史记录
        bidRequestAuditHistoryService.save(br, BidRequestAuditHistory.AUDITTYPE_PUBLISH, state, remark);
        br.setNote(remark);

        if (state == BaseAuditDomain.STATE_SUCCESS) {
            //审核成功
            if (publishTime != null) {
                //修改位状态为待发标
                br.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_PENDING);
                br.setPublishTime(publishTime);
            } else {
                //设置待发标, 借款对象状态为招标中
                br.setBidRequestState(Constants.BIDREQUEST_STATE_BIDDING);
                br.setPublishTime(new Date());
            }
            //招标截止时间
            br.setDisableDate(DateUtils.addDays(br.getPublishTime(), br.getDisableDays()));
        } else {
            //审核失败
            UserInfo info = userInfoService.getById(br.getCreateUser().getId());
            //移除userinfo中的位状态
            info.remove(BitStatesUtil.HAS_BIDREQUEST_IN_PROCESS);
            userInfoService.update(info);
            //修改借款对象状态为发表审核失败状态
            br.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_REFUSE);
        }
        update(br);

        //审核通过,状态为待发标,加入到缓存对列中
        if (state == BaseAuditDomain.STATE_SUCCESS && publishTime != null) {
            br.setVersion(br.getVersion() + 1);
            boolean b = linkedQueue.offer(br);

        }

    }

    /**
     * 查询所有的状态为待发布的加入缓存序列
     */
    public void publishBidRequestToQueue() {
        BidRequestQuery qo = new BidRequestQuery();
        qo.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_PENDING);
        List<BidRequest> brs = bidRequestMapper.selectForList(qo);
        for (BidRequest br : brs) {
            //去重
            if (!linkedQueue.contains(br)) {
                linkedQueue.offer(br);
            }
        }
        System.out.println("init linkedQueue size :" + linkedQueue.size());
    }

    /**
     * 缓冲中取,过了时间则移除
     */
    @Override
    public void checkPublishBidRequestforQueue() {
        Iterator<BidRequest> it = linkedQueue.iterator();
        Date now = new Date();
        while (it.hasNext()) {
            BidRequest br = it.next();
            if (br.getPublishTime().before(now)) {
                br.setBidRequestState(Constants.BIDREQUEST_STATE_BIDDING);
                update(br);
                it.remove();
            }
        }
    }

    public void update(BidRequest br) {
        AssertUtil.instance().isFalse(bidRequestMapper.updateByPrimaryKey(br) == 0, "借款信息修改失败,乐观锁异常[" + br.getId() + "]");
    }

    /**
     * 系统启动时查出所有待发标的放进缓存中
     *
     * @param contextRefreshedEvent
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        publishBidRequestToQueue();
    }

    @Override
    public BidRequest getById(Long id) {
        return bidRequestMapper.selectByPrimaryKey(id);
    }

    @Override
    public void bid(Long bidRequestId, BigDecimal amount) {
        //判断参数
        //投标金额不能为空,
        AssertUtil.instance().isNotNull(amount, "投标金额不能为空");
        BidRequest br = bidRequestMapper.selectByPrimaryKey(bidRequestId);
        AssertUtil.instance().isNotNull(br, "无效的借款")
                //借款状态招标中
                .isFalse(br.getBidRequestState() != Constants.BIDREQUEST_STATE_BIDDING, "标不在招标中");
        LoginInfo currentUser = UserContext.getCurrentUser();
        //借款对象,本人不能投标
        AssertUtil.instance().isFalse(br.getCreateUser().getId().longValue() == currentUser.getId(), "招标与投标不能为一人");
        Account account = accountService.getById(currentUser.getId());
        //投标金额<=可用余额
        AssertUtil.instance().isFalse(amount.compareTo(account.getUsableAmount()) >= 0, "可用余额不足");
        //对这次投标额不能超过借款的1/2
        BigDecimal totalBid = bidService.getUserBid(br.getId(), currentUser.getId());
        // 投标金额不能小于最小投标金额(万一最后剩余招标金额小于了最小投标金额,则一次投满)
        AssertUtil.instance().isFalse(totalBid.compareTo(br.getBidRequestAmount().multiply(Constants.FIVE)) > 0, "投标金额不能超过借款的1/2");
        if (br.getRemainAmount().compareTo(br.getMinBidAmount()) < 0) {
            AssertUtil.instance().isFalse(amount.compareTo(br.getMinBidAmount()) != 0, "当前投标仅需一次投入" + br.getRemainAmount());
        } else {
            AssertUtil.instance().isFalse(amount.compareTo(br.getMinBidAmount()) < 0, "投标金额不能低于最小投标金额:" + br.getMinBidAmount());
        }
        //创建投标记录
        bidService.save(amount, br, currentUser);
        //个人账户
        //可用余额减少, 冻结资金增加
        account.subtractUsableAmount(amount);
        account.addFreezedAmount(amount);
        accountService.update(account);
        //投标流水
        accountFlowService.createBidFlow(account, amount);
        //借款对象
        // 当前筹款总额
        br.setCurrentSum(br.getCurrentSum().add(amount));
        // 投标次数
        br.setBidCount(br.getBidCount() + 1);
        // 改变状态
        if (br.getCurrentSum().compareTo(br.getBidRequestAmount()) == 0) {
            //设置借款对象状态满标1审
            br.setBidRequestState(Constants.BIDREQUEST_STATE_APPROVE_PENDING_1);
            //批量跟新bid记录里为满标一审状态
            bidService.batchBidRequestState(br.getId(), Constants.BIDREQUEST_STATE_APPROVE_PENDING_1);
        }
        update(br);
    }

    @Override
    public void fullAudit1(Long id, Integer state, String remark) {
        //判断当前是否处于满标一审
        BidRequest br = bidRequestMapper.selectByPrimaryKey(id);
        AssertUtil.instance().isFalse(br.getBidRequestState() != Constants.BIDREQUEST_STATE_APPROVE_PENDING_1, "借款状态不属于满标一审");
        //设置审核信息,保存审核记录
        bidRequestAuditHistoryService.save(br, BidRequestAuditHistory.AUDITTYPE_AUDIT1, state, remark);
        if (state == BaseAuditDomain.STATE_SUCCESS) {
            //审核成功,修改为满标二审
            br.setBidRequestState(Constants.BIDREQUEST_STATE_APPROVE_PENDING_2);
            //投标对象改为满标2审
            bidService.batchBidRequestState(br.getId(), Constants.BIDREQUEST_STATE_APPROVE_PENDING_2);
        } else {
            fullAuditReject(br);
        }
        update(br);
    }

    @Override
    public void fullAudit2(Long id, Integer state, String remark) {
        //判断参数
        BidRequest br = bidRequestMapper.selectByPrimaryKey(id);
        UserInfo userInfo = userInfoService.getById(br.getCreateUser().getId());
        AssertUtil.instance().isFalse(br.getBidRequestState() != Constants.BIDREQUEST_STATE_APPROVE_PENDING_2, "借款状态不属于满标二审");
        //设置审核信息,保存审核记录
        bidRequestAuditHistoryService.save(br, BidRequestAuditHistory.AUDITTYPE_AUDIT2, state, remark);
        if (state == BaseAuditDomain.STATE_SUCCESS) {
            //审核成功,修改为还款中
            br.setBidRequestState(Constants.BIDREQUEST_STATE_PAYING_BACK);
            //投标对象改为还款中
            bidService.batchBidRequestState(br.getId(), Constants.BIDREQUEST_STATE_PAYING_BACK);
            //移除借款申请中的位状态
            userInfo.remove(BitStatesUtil.HAS_BIDREQUEST_IN_PROCESS);
            userInfoService.update(userInfo);
            //借款人的可用余额增加
            Account account = accountService.getById(br.getId());
            account.addUsableAmount(br.getBidRequestAmount());
            //创建借款人的借款流水
            accountFlowService.createBidRequestFlow(account, br);
            //借款人的状态改为还款中
            br.setBidRequestState(Constants.BIDREQUEST_STATE_PAYING_BACK);
            //借款人的授信额度减少
            account.setRemainBorrowLimit(account.getRemainBorrowLimit().subtract(br.getBidRequestAmount()));
            //借款人的待还本息增加
            account.setUnReturnAmount(account.getUnReturnAmount().add(br.getBidRequestAmount()).add(br.getTotalRewardAmount()));
            //借款平台管理费
            BigDecimal charge = CalculatetUtil.calAccountManagementCharge(br.getBidRequestAmount());
            account.subtractUsableAmount(charge);
            //借款平台的流水
            accountFlowService.createPayBorrowManagementChargeFlow(charge, account, br);
            Map<Long, Account> bidAccounts = new HashMap<>();
            //投资人
            List<Bid> bids = br.getBids();
            for (Bid bid : bids) {
                Long bidUserId = bid.getBidUser().getId();
                Account bidAccount = bidAccounts.get(bidUserId);
                if (bidAccount == null) {
                    bidAccount = accountService.getById(bidUserId);
                    bidAccounts.put(bidUserId, bidAccount);
                }
                //冻结金额减少
                bidAccount.subtractFreezedAmount(bid.getAvailableAmount());
                //创建流水
                accountFlowService.createFreezedAmountFlow(bid.getAvailableAmount(), bidAccount, br);
                //投资人待收本金
                bidAccount.setUnReturnAmount(bidAccount.getUnReturnAmount().add(bid.getAvailableAmount()));
                //代收利息 = 当前投标本金/借款金额*借款总利息
                BigDecimal unReceiveInterest = bid.getAvailableAmount().divide(br.getBidRequestAmount(), Constants.CAL_SCALE, RoundingMode.HALF_UP)
                        .multiply(br.getTotalRewardAmount());
                bidAccount.setUnReceiveInterest(bidAccount.getUnReceiveInterest().add(unReceiveInterest.setScale(Constants.STORE_SCALE, BigDecimal.ROUND_HALF_UP)));
            }
            //创建还款计划
            List<PaymentSchedule> pss = paymentScheduleService.createPaymentSchedule(br);
            //创建收款计划
            for (PaymentSchedule ps : pss) {
                List<PaymentScheduleDetail> details = ps.getPaymentScheduleDetails();
                for (PaymentScheduleDetail detail : details) {
                    Account bAccount = bidAccounts.get(detail.getToLoginInfoId());
                    bAccount.setUnReceiveInterest(bAccount.getUnReceiveInterest().add(detail.getInterest()));
                    bAccount.setUnReceivePrincipal(bAccount.getUnReceivePrincipal().add(detail.getPrincipal()));
                }
                //修改投资人账户
                for (Account a : bidAccounts.values()) {
                    accountService.update(a);
                }
                //平台
                SystemAccount systemAccount = systemAccountService.getCurrent();
                //平台收到借款人的管理费,可用余额增加
                systemAccount.setUsableAmount(systemAccount.getUsableAmount().add(charge));
                systemAccountService.update(systemAccount);
                //创建平台收取管理费流水
                systemAccountFlowService.createBorrowManagementChargeFlow(systemAccount, charge, br);
            }


        } else {
            //审核失败
            fullAuditReject(br);
        }
        update(br);
    }

    @Override
    public void returnMoney(Long id) {
        //判断参数
        //判断当前还款计划是否处于待还或逾期
        PaymentSchedule ps = paymentScheduleService.getById(id);
        AssertUtil.instance().isFalse(ps.getState() == Constants.PAYMENT_STATE_DONE, "当前期已环");
        //当前用户
        LoginInfo currentUser = UserContext.getCurrentUser();
        Account account = accountService.getById(currentUser.getId());
        //判断该账户的可用余额
        AssertUtil.instance().isFalse(account.getUsableAmount().compareTo(ps.getTotalAmount()) < 0, "账户余额不足");
        //借款人,只有借款人才能还款
        LoginInfo borrowUser = ps.getBorrowUser();
        AssertUtil.instance().isFalse(currentUser.getId().longValue() != borrowUser.getId().longValue(), "不能帮别人还款");
        //执行还款
        //借款人可用余额减少
        account.subtractUsableAmount(ps.getTotalAmount());
        //创建还款流水
        accountFlowService.createReturnMoneyFlow(account, ps.getTotalAmount(), ps);
        //修改还款计划为已还
        ps.setState(Constants.PAYMENT_STATE_DONE);
        //设置还款时间
        Date now = new Date();
        ps.setPayDate(now);
        paymentScheduleService.update(ps);
        //借款人的剩余授信额度增加
        account.setRemainBorrowLimit(account.getRemainBorrowLimit().add(ps.getPrincipal()));
        //借款人的待还本息减少
        account.setUnReturnAmount(account.getUnReturnAmount().subtract(ps.getTotalAmount()));
        accountService.update(account);

        //投资人
        List<PaymentScheduleDetail> details = ps.getPaymentScheduleDetails();
        //系统账户
        SystemAccount systemAccount = systemAccountService.getCurrent();
        Map<Long, Account> bidAccounts = new HashMap<>();
        for (PaymentScheduleDetail psd : details) {
            //收款人id
            Long bidUserId = psd.getToLoginInfoId();
            //收款人账户
            Account bidAccount = bidAccounts.get(bidUserId);
            if (bidAccount == null) {
                bidAccount = accountService.getById(bidUserId);
                bidAccounts.put(bidUserId, bidAccount);
            }
            //收款.可用余额增加
            bidAccount.addUsableAmount(psd.getTotalAmount());
            //创建收款流水
            accountFlowService.createReceiveFlow(bidAccount, psd.getTotalAmount(), ps);
            //待收本金减少
            bidAccount.setUnReceivePrincipal(bidAccount.getUnReceivePrincipal().subtract(psd.getPrincipal()));
            //待收利息减少
            bidAccount.setUnReceiveInterest(bidAccount.getUnReceiveInterest().subtract(psd.getInterest()));
            //收款计划修改收款时间
            psd.setPayDate(now);
            paymentScheduleDetailService.update(psd);
            //支付平台利息管理费
            BigDecimal managerCharge = CalculatetUtil.calInterestManagerCharge(psd.getInterest());
            //创建支付信息管理费流水
            accountFlowService.createManagerChargeFlow(bidAccount, managerCharge);

            //平台 收取利息管理费
            systemAccount.setUsableAmount(systemAccount.getUsableAmount().add(managerCharge));
            //创建利息管理费流水
            systemAccountFlowService.createInterestManagerChargeFlow(systemAccount, managerCharge);

            /**
             * 债权标的取消:
             * 如果这个 psd 的 transferState 在转让中,那么这个收款计划已经被打包在了一个转让中的债权标中
             * 我们需要:
             * 1.根据psd 获取到关联的债权标对象 ,修改状态为撤销
             *
             * 2.再根据这个债权标对象,去找到他下面的打包的多个收款计划,把债权转让状态修改为正常
             */
            if (psd.getTransferState() == PaymentScheduleDetail.TRANSFER_STATE_TRANSFERING) {
                //1.根据psd 获取到关联的债权标对象 ,
                CreditTransfer transfer = creditTransferService
                        .getByBidIdAndTransferFromIdAndState(psd.getBidId(), psd.getToLoginInfoId(), Constants.BIDREQUEST_STATE_BIDDING);

                // 修改状态为撤销
                transfer.setBidRequestState(Constants.BIDREQUEST_STATE_UNDO);

                creditTransferService.update(transfer);


                //2.再根据这个债权标对象,去找到他下面的打包的多个收款计划,把债权转让状态修改为正常
                paymentScheduleDetailService
                        .batchUpdateTransferState(new Long[]{psd.getBidId()}, PaymentScheduleDetail.TRANSFER_STATE_NORMAL);
            }

        }
        systemAccountService.update(systemAccount);
        //修改投资人的账户
        for (Account bidAccount : bidAccounts.values()) {
            accountService.update(bidAccount);
        }
        //6.如果已经还清
        int returnNumber = paymentScheduleService.getReturnSuccessMonthIndexNumber(ps.getBidRequestId(), Constants.PAYMENT_STATE_DONE);
        BidRequest br = getById(ps.getBidRequestId());
        if (returnNumber == br.getMonthes2Return()) {
            //1.修改借款对象状态为已还清
            br.setBidRequestState(Constants.BIDREQUEST_STATE_COMPLETE_PAY_BACK);
            update(br);

            //2.这个借款对应的投标对象状态修改为已还清
            bidService.batchUpdateStateByBidRequestId(br.getId(), Constants.BIDREQUEST_STATE_COMPLETE_PAY_BACK);
        }

    }

    //满标一审/二审 审核失败
    private void fullAuditReject(BidRequest br) {
        //审核失败,修改借款对象状态为满标审核拒绝
        br.setBidRequestState(Constants.BIDREQUEST_STATE_REJECTED);
        //投标对象状态改为拒绝
        bidService.batchBidRequestState(br.getId(), Constants.BIDREQUEST_STATE_REJECTED);
        //投资人账户缓存
        Map<Long, Account> bidAccounts = new HashMap<>();
        //退钱
        List<Bid> bids = new ArrayList<>();
        for (Bid bid : bids) {
            //投资人id
            Long bidLoginInfoId = bid.getBidUser().getId();
            //投资人账户
            Account bidAccount = bidAccounts.get(bidLoginInfoId);
            if (bidAccount == null) {
                bidAccount = accountService.getById(bidLoginInfoId);
                bidAccounts.put(bidLoginInfoId, bidAccount);
            }
            //投标人冻结金额减少
            bidAccount.subtractFreezedAmount(bid.getAvailableAmount());
            //可用余额增加
            bidAccount.addUsableAmount(bid.getAvailableAmount());
            //生成失败投标流水
            accountFlowService.createBidFailureFlow(bidAccount, bid.getAvailableAmount(), br);
        }
        //修改投资人账户
        for (Account bidAccount : bidAccounts.values()) {
            accountService.update(bidAccount);
        }
        //移除借款人是否在申请中的位状态
        UserInfo info = userInfoService.getById(br.getCreateUser().getId());
        info.remove(BitStatesUtil.HAS_BIDREQUEST_IN_PROCESS);
        userInfoService.update(info);
    }
}
